/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef I2C_DRV_H
#define I2C_DRV_H

/*!
 * @file i2c_drv.h
 * @brief This file declares i2c driver interfaces
 */

/*!
 * @addtogroup i2c_drv
 * @{
 */

/*******Includes***************************************************************/
#include <stddef.h>
#include <stdbool.h>
#include "dma_drv.h"
#include "Os_Adapter.h"
#include "errcodes.h"

/*******Definitions************************************************************/
/*!
 * @brief I2C baudrate configuration structure
 */
typedef struct {
    uint8_t i2cBaudPrescaler;      /*!< I2C baud prescaler */
    uint8_t i2cBaudSampleWidth;   /*!< I2C Baud sample width */
} i2c_baudrate_div_t;


/*!
 * @brief I2C types of mode
 */
typedef enum {
    I2C_MODE_SLAVE = 0U,    /*!< I2C slave mode */
    I2C_MODE_MASTER = 1U    /*!< I2C master mode */
} i2c_mode_t;

/*!
 * @brief I2C types of timeout detected mode
 */
typedef enum {
    I2C_BUS_TIMEOUT_DISABLE = 0U,   /*!< Timeout detection is diable */
    I2C_BUS_TIMEOUT_SCL = 1U,       /*!< Timeout detection only SCL */
    I2C_BUS_TIMEOUT_SCL_SDA = 2U    /*!< Timeout detection both SCL and SDA */
} i2c_bus_timeout_t;

/*!
 * @brief I2C types of transfer mode
 */
typedef enum {
    I2C_USING_INTERRUPT  = 0U,    /*!< I2C transfers by interrupt */
    I2C_USING_DMA = 1U            /*!< I2C transfers by DMA */
} i2c_transfer_type_t;

/*!
 * @brief I2C types of transfer mode
 */
typedef enum {
    I2C_SLAVE_EVENT_TX_REQ          = 0x00U, /*!< Slave tx request event*/
    I2C_SLAVE_EVENT_RX_REQ          = 0x01U, /*!< Slave rx request event*/
    I2C_EVENT_END_TRANSFER          = 0x02U, /*!< End transfer events */
    I2C_SLAVE_EVENT_RX_OVERFLOW     = 0x03U, /*!< Slave Transfer data overflow */
    I2C_SLAVE_EVENT_TX_UNDERFLOW    = 0x04U, /*!< Slave received data underflow */
    I2C_EVENT_DMA_TRANSFER_ERROR    = 0x05U, /*!< I2C dma transfer error */
    I2C_EVENT_LOW_TIMEOUT           = 0x06U, /*!< I2C pin low timeout */
    I2C_EVENT_STOP                  = 0x07U, /*!< Stop events */
    I2C_EVENT_BUFFER_INVALID        = 0x08U, /*!< Slave buffer not prepare*/
} i2c_event_t;

/*!
 * @brief I2C callback type
 */
typedef void (*i2c_callback_t)(uint32_t instance, i2c_event_t event, void* param);

/*!
* @brief Master configuration structure
*/
typedef struct {
    uint16_t            slaveAddress;       /*!< Slave address */
    bool                is10bitAddr;        /*!< Is 10bit address mode */
    i2c_transfer_type_t transferType;       /*!< Transfer mode: dma or interrupt */
    uint8_t             dmaChannel;         /*!< Dma tx or rx chanel id */
    uint8_t             glitchFilterCnt;    /*!< Glitch filter Cnt */
    i2c_baudrate_div_t  baud;               /*!< I2C baud division coefficient */
    bool                isMasterArb;        /*!< Whether to enable the master arbitration */
    bool                isMasterSclSyc;     /*!< Whether to enable the master clock Synchronization */
    i2c_callback_t      masterCallback;     /*!< User callback */
    void*               callbackParam;      /*!< Parameter for the callback function */
} i2c_master_config_t;

/*!
 * @brief Slave configuration structure
 */
typedef struct {
    uint16_t            slaveAddress;    /*!< Slave address */
    bool                is10bitAddr;     /*!< Is 10bit address mode */
    i2c_transfer_type_t transferType;    /*!< Transfer mode: dma or interrupt */
    uint8_t             dmaChannel;      /*!< Dma tx or rx chanel id */
    uint8_t             glitchFilterCnt; /*!< Glitch filter Cnt */
    bool                isSlaveStretch;  /*!< Whether to enable the slave stretch */
    bool                isGeneralCall;   /*!< Whether to enable general call address */
    bool                alertAddress;    /*!< Whether to enable alert address */
    i2c_callback_t      slaveCallback;   /*!< User callback */
    void*               callbackParam;   /*!< Parameter for the callback function */
    bool                slaveListening; /*!< Always listening mode or demand only*/
} i2c_slave_config_t;

/*!
 * @brief master internal context structure
 */
typedef struct {
    uint8_t*            rxBuffer;       /*!< Receive buffer */
    uint32_t            rxSize;         /*!< Receive buffer size */
    const uint8_t*      txBuffer;       /*!< Transfer buffer */
    uint32_t            txSize;         /*!< Transfer buffer size */
    volatile errcode_t  errCode;        /*!< Record error status */
    bool                is10bitAddr;    /*!< Is 10bit address mode */
    i2c_transfer_type_t transferType;   /*!< Transfer mode: dma or interrupt */
    uint8_t             dmaChannel;     /*!< Dma tx or rx chanel id*/
    bool                blocking;       /*!< If the current transfer is blocking */
    semaphore_t         idleSemaphore;  /*!< Semaphore used by blocking functions */
    i2c_callback_t      callback;       /*!< Callback function */
    void*               callbackParam;  /*!< Parameter for the callback function */
    uint16_t            slaveAddress;   /*!< Slave address */
    volatile bool       i2cIdle;        /*!< Idle/busy state of the driver */
    bool                sendStop;       /*!< Specifies if STOP condition will be generated */
} i2c_master_state_t;

/*!
 * @brief slave internal context structure
 */
typedef struct {
    uint8_t*            rxBuffer;               /*!< Receive buffer */
    uint32_t            rxSize;                 /*!< Receive buffer size */
    const uint8_t*      txBuffer;               /*!< Transfer buffer */
    uint32_t            txSize;                 /*!< Transfer buffer size */
    volatile errcode_t  errCode;                /*!< Record error status */
    bool                is10bitAddr;            /*!< Is 10bit address mode */
    i2c_transfer_type_t transferType;           /*!< Transfer mode: dma or interrupt */
    uint8_t             dmaChannel;             /*!< Dma tx or rx chanel id*/
    bool                blocking;               /*!< If the current transfer is blocking */
    semaphore_t         idleSemaphore;          /*!< Semaphore used by blocking functions */
    i2c_callback_t      callback;               /*!< Callback function */
    void*               callbackParam;          /*!< Parameter for the callback function */
    volatile bool       isTransferInProgress;   /*!< Slave is busy because an ongoing transfer */
    bool                isSlaveStretch;         /*!< Whether to enable the slave stretch function */
    bool                slaveListening;         /*!< Always listening mode or demand only*/
} i2c_slave_state_t;


/*******APIs*******************************************************************/
#if defined(__cplusplus)
extern "C" {
#endif

/*!
 * @brief Initialize the I2C master mode driver
 *
 * @param[in] instance: I2C instance number
 * @param[out] master: Pointer to the I2C master driver context structure,
 *                     this point will be reserved for asynchronously use,
 *                     it must be pointed to static or global memory
 * @param[in] i2cConfig: Pointer to the I2C master user configuration structure
 * @return None
 */
void I2C_MasterInit(uint32_t instance, i2c_master_state_t* master, const i2c_master_config_t* i2cConfig);

/*!
 * @brief Deinitialize the I2C master mode driver
 *
 * @param[in] instance: The I2C instance number
 * @return None
 */
void I2C_MasterDeinit(uint32_t instance);

/*!
 * @brief Get the currently configured baud rate
 *
 * @param[in] instance: The I2C instance number
 * @param[out] Baudrate: The current baud rate in hertz
 * @return None
 */
void I2C_MasterGetBaudRate(uint32_t instance, uint32_t* baudrate);

/*!
 * @brief Set the baud rate
 *
 * @param[in] instance: The I2C instance number
 * @param[in] baud: The parameter of baudrate
 * @return - ERR_SUCCESS: Baudrate is set successfully
 *         - ERR_ERROR: Baudrate is not set successfully
 */
errcode_t I2C_MasterSetBaudRate(uint32_t instance, i2c_baudrate_div_t baud);

/*!
 * @brief Gets the default configuration structure for master
 *
 * @param[in] instance: The I2C instance number
 * @param[out] config: Pointer to configuration structure
 * @return None
 */
void I2C_MasterGetDefaultConfig(i2c_master_config_t* config);

/*!
 * @brief Set the slave address in master mode
 *
 * @param[in] instance: The I2C instance number
 * @param[in] address: slave address
 * @param[in] is10bitAddr: whether provided address is 10-bit
 * @return None
 */
void I2C_MasterSetSlaveAddr(uint32_t instance, uint16_t address, bool is10bitAddr);

/*!
 * @brief Perform a non-blocking send transaction on the I2C bus
 *
 * @param[in] instance: The I2C instance number
 * @param[in] txBuff: pointer to the data to be transferred
 * @param[in] txSize: length in bytes of the data to be transferred
 * @param[in] sendStop: whether or not to generate stop condition after the transmission
 * @return Error or success status
 */
errcode_t I2C_MasterSendData(uint32_t instance, const uint8_t* txBuff, uint32_t txSize, bool sendStop);

/*!
 * @brief Perform a blocking send transaction on the I2C bus
 *
 * @param[in] instance: The I2C instance number
 * @param[in] txBuff: pointer to the data to be transferred
 * @param[in] txSize: length in bytes of the data to be transferred
 * @param[in] sendStop: whether or not to generate stop condition after the transmission
 * @param[in] timeout: timeout for the transfer in milliseconds
 * @return Error or success status
 */
errcode_t I2C_MasterSendDataBlocking(uint32_t instance, const uint8_t* txBuff,
                                     uint32_t txSize, bool sendStop, uint32_t timeout);

/*!
* @brief Perform a Polling send transaction on the I2C bus
*
* @param[in] instance: The I2C instance number
* @param[in] txBuff: pointer to the data to be transferred
* @param[in] txSize: length in bytes of the data to be transferred
* @param[in] sendStop: whether or not to generate stop condition after the transmission
* @return Error or success status
*/
errcode_t I2C_MasterSendDataPolling(uint32_t instance, const uint8_t* txBuff,
                                    uint32_t txSize, bool sendStop);

/*!
 * @brief Perform a non-blocking receive transaction on the I2C bus
 *
 * @param[in] instance: The I2C instance number
 * @param[out] rxBuff: pointer to the buffer where to store received data
 * @param[in] rxSize: length in bytes of the data to be transferred
 * @param[in] sendStop: specifies whether or not to generate stop condition after the reception
 * @return Error or success status
 */
errcode_t I2C_MasterReceiveData(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize, bool sendStop);

/*!
 * @brief Perform a non-blocking receive transaction on the I2C bus
 *
 * @param[in] instance: The I2C instance number
 * @param[in] rxBuff: pointer to the buffer where to store received data
 * @param[out] rxSize: length in bytes of the data to be transferred
 * @param[in] sendStop: specifies whether or not to generate stop condition after the reception
 * @param[in] timeout: timeout for the transfer in milliseconds
 * @return Error or success status
 */
errcode_t I2C_MasterReceiveDataBlocking(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize, bool sendStop, uint32_t timeout);


/*!
 * @brief Perform a Polling receive transaction on the I2C bus
 *
 * @param[in] instance: The I2C instance number
 * @param[in] rxBuff: pointer to the buffer where to store received data
 * @param[out] rxSize: length in bytes of the data to be transferred
 * @param[in] sendStop: specifies whether or not to generate stop condition after the reception
 * @return Error or success status
 */
errcode_t I2C_MasterReceiveDataPolling(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize, bool sendStop);

/*!
 * @brief Return the current status of the I2C master transfer
 *
 * @param[in] instance: The I2C instance number
 * @param[out] byteRemaining: the number of remaining bytes in the active I2C transfer
 * @return i2c transfer status
 */
errcode_t I2C_MasterGetTransferStatus(uint32_t instance, uint32_t* byteRemaining);

/*!
 * @brief Abort a non-blocking I2C Master transmission or reception
 *
 * @param[in] instance: The I2C instance number
 * @return None
 */
void I2C_MasterAbortTransferData(uint32_t instance);

/*!
 * @brief Initialize the I2C slave mode driver
 *
 * @param[in] instance: I2C instance number
 * @param[out] slave: Pointer to the I2C slave driver context structure
 *                    this point will be reserved for asynchronously use,
 *                    it must be pointed to static or global memory
 * @param[in] i2cConfig: Pointer to the I2C slave user configuration structure
 * @return None
 */
void I2C_SlaveInit(uint32_t instance, i2c_slave_state_t* slave, const i2c_slave_config_t* i2cConfig);

/*!
 * @brief Deinitialize the I2C slave mode driver
 *
 * @param[in] instance: The I2C instance number
 * @return None
 */
void I2C_SlaveDeinit(uint32_t instance);

/*!
 * @brief Perform a non-blocking send transaction on the I2C bus
 *
 * @param[in] instance: The I2C instance number
 * @param[in] txBuff: pointer to the data to be transferred
 * @param[in] txSize: length in bytes of the data to be transferred
 * @return Error or success status
 */
errcode_t I2C_SlaveSendData(uint32_t instance, const uint8_t* txBuff, uint32_t txSize);

/*!
 * @brief Perform a blocking send transaction on the I2C bus
 *
 * @param[in] instance: The I2C instance number
 * @param[in] txBuff: pointer to the data to be transferred
 * @param[in] txSize: length in bytes of the data to be transferred
 * @param[in] timeout: timeout for the transfer in milliseconds
 * @return Error or success status
 */
errcode_t I2C_SlaveSendDataBlocking(uint32_t instance, const uint8_t* txBuff, uint32_t txSize, uint32_t timeout);

/*!
 * @brief Perform a non-blocking receive transaction on the I2C bus
 *
 * @param[in] instance: The I2C instance number
 * @param[out] rxBuff: pointer to the buffer where to store received data
 * @param[in] rxSize: length in bytes of the data to be transferred
 * @return Error or success status
 */
errcode_t I2C_SlaveReceiveData(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize);

/*!
 * @brief Perform a non-blocking receive transaction on the I2C bus
 *
 * @param[in] instance: The I2C instance number
 * @param[in] rxBuff: pointer to the buffer where to store received data
 * @param[out] rxSize: length in bytes of the data to be transferred
 * @param[in] timeout: timeout for the transfer in milliseconds
 * @return Error or success status
 */
errcode_t I2C_SlaveReceiveDataBlocking(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize, uint32_t timeout);

/*!
 * @brief Provide a buffer for receiving data. This API only used in listening mode.
 *
 * @param[in] instance: The I2C instance number
 * @param[in] rxBuff: pointer to the buffer where to store received data
 * @param[out] rxSize: length in bytes of the data to be received
 * @return Error or success status
 */
errcode_t I2C_SlaveSetRxBuffer(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize);

/*!
 * @brief Provide a buffer for transmitting data. This API only used in listening mode.
 *
 * @param[in] instance: The I2C instance number
 * @param[in] txBuff: pointer to the buffer where to store transfered data
 * @param[out] txSize: length in bytes of the data to be transfered
 * @return Error or success status
 */
errcode_t I2C_SlaveSetTxBuffer(uint32_t instance, uint8_t* txBuff, uint32_t txSize);

/*!
 * @brief Return the current status of the I2C slave transfer
 *
 * @param[in] instance: The I2C instance number
 * @param[out] byteRemaining: the number of remaining bytes in the active I2C transfer
 * @return i2c transfer status
 */
errcode_t I2C_SlaveGetTransferStatus(uint32_t instance, uint32_t* byteRemaining);

/*!
 * @brief Abort a non-blocking I2C Slave transmission or reception
 *
 * @param[in] instance: The I2C instance number
 * @return None
 */
void I2C_SlaveAbortTransferData(uint32_t instance);

/*!
 * @brief Gets the default configuration structure for slave
 *
 * @param[in] instance: The I2C instance number
 * @param[out] config: Pointer to configuration structure
 * @return None
 */
void I2C_SlaveGetDefaultConfig(i2c_slave_config_t* config);

/*!
 * @brief Set bus low timeout detection
 *
 * @param[in] instance: The I2C instance number
 * @param[in] mode: The mode of timeout detection(disable/SCL/SCL&SDA)
 * @param[in] timeout: low detect value
 * @return - ERR_SUCCESS: bus low timeout is set successfully
 *         - ERR_ERROR: bus low timeout is not set successfully
 */
errcode_t I2C_SetBusTimeoutDetection(uint32_t instance, i2c_bus_timeout_t mode, uint16_t threshold);

/*!
 * @brief Reset the hardware transfer logic
 *
 * @param[in] instance: The I2C instance number
 * @return None
 */
void I2C_ResetHwTransferLogic(uint32_t instance);

/*!
 * @brief Register callback function and its parameter for i2c instance
 *
 * @param[in] instance: I2C instance
 * @param[in] callback: Pointer to the callback function
 * @param[in] param: Pointer to the callback function's parameter
 * @return None
 */
void I2C_InstallCallback(uint32_t instance, i2c_callback_t callback, void* param);

/*!
 * @brief I2C interrupt handler
 *
 * @param[in] instance: The I2C instance number
 * @return None
 */
void I2C_IRQHandlerProcess(uint32_t instance);

#if defined(__cplusplus)
}
#endif

/*! @} */

#endif /* I2C_DRV_H */

/*******EOF********************************************************************/
